<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>XML格式化工具</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #e0f7fa, #f5f5f5);
            color: #333;
            min-height: 100vh;
            padding: 20px;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
        }

        header {
            text-align: center;
            margin-bottom: 30px;
            padding: 20px;
        }

        h1 {
            color: #006064;
            font-size: 2.8rem;
            margin-bottom: 10px;
            text-shadow: 1px 1px 2px rgba(0,0,0,0.1);
        }

        .subtitle {
            color: #00838f;
            font-size: 1.2rem;
            max-width: 600px;
            margin: 0 auto;
        }

        .panel-container {
            display: flex;
            flex-direction: column;
            gap: 20px;
            margin-bottom: 30px;
        }

        .panel {
            background: rgba(255, 255, 255, 0.85);
            border-radius: 10px;
            box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
            overflow: hidden;
            transition: all 0.3s ease;
        }

        .panel-header {
            background: linear-gradient(to right, #00838f, #006064);
            color: white;
            padding: 15px 20px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            cursor: pointer;
            user-select: none;
        }

        .panel-title {
            font-size: 1.4rem;
            font-weight: 500;
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .panel-actions {
            display: flex;
            gap: 10px;
        }

        .btn {
            background: rgba(255, 255, 255, 0.2);
            border: none;
            color: white;
            padding: 8px 15px;
            border-radius: 5px;
            cursor: pointer;
            font-weight: 500;
            transition: all 0.2s;
            display: flex;
            align-items: center;
            gap: 5px;
        }

        .btn:hover {
            background: rgba(255, 255, 255, 0.3);
            transform: translateY(-2px);
        }

        .btn-icon {
            font-size: 1.1rem;
        }

        .panel-content {
            padding: 20px;
            transition: all 0.3s ease;
        }

        .collapsed .panel-content {
            display: none;
        }

        #xml-input {
            width: 100%;
            height: 200px;
            padding: 15px;
            border: 1px solid #b2ebf2;
            border-radius: 8px;
            font-family: 'Consolas', 'Courier New', monospace;
            font-size: 14px;
            background: rgba(178, 235, 242, 0.1);
            resize: vertical;
            transition: all 0.3s;
        }

        #xml-input:focus {
            outline: none;
            border-color: #00838f;
            box-shadow: 0 0 0 3px rgba(0, 131, 143, 0.2);
        }

        #formatted-output {
            background: rgba(178, 235, 242, 0.1);
            border: 1px solid #b2ebf2;
            border-radius: 8px;
            padding: 20px;
            font-family: 'Consolas', 'Courier New', monospace;
            font-size: 14px;
            white-space: pre-wrap;
            word-break: break-word;
            min-height: 300px;
            max-height: 500px;
            overflow: auto;
        }

        .xml-tag {
            color: #006064;
            font-weight: bold;
        }

        .xml-attr-name {
            color: #d81b60;
        }

        .xml-attr-value {
            color: #00838f;
        }

        .xml-text {
            color: #558b2f;
        }

        .xml-comment {
            color: #9e9e9e;
            font-style: italic;
        }

        .single-line {
            display: inline;
        }

        .error {
            color: #c62828;
            background: rgba(198, 40, 40, 0.1);
            padding: 15px;
            border-radius: 5px;
            margin-top: 15px;
            font-family: monospace;
            white-space: pre-wrap;
        }

        .features {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 20px;
            margin-top: 30px;
        }

        .feature-card {
            background: rgba(255, 255, 255, 0.8);
            border-radius: 10px;
            padding: 20px;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08);
            transition: transform 0.3s ease;
        }

        .feature-card:hover {
            transform: translateY(-5px);
        }

        .feature-card h3 {
            color: #006064;
            margin-bottom: 10px;
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .feature-icon {
            font-size: 1.5rem;
            color: #00838f;
        }

        footer {
            text-align: center;
            margin-top: 40px;
            padding: 20px;
            color: #00838f;
            font-size: 0.9rem;
        }

        @media (max-width: 768px) {
            h1 {
                font-size: 2.2rem;
            }

            .panel-actions {
                flex-direction: column;
                gap: 8px;
            }

            .btn {
                width: 100%;
            }
        }
    </style>
</head>
<body>
<div class="container">
    <header>
        <h1>XML 格式化工具</h1>
        <p class="subtitle">输入XML内容，获取美观易读的格式化结果。支持折叠输入区域，自动换行长文本</p>
    </header>

    <div class="panel-container">
        <div class="panel" id="input-panel">
            <div class="panel-header">
                <div class="panel-title">
                    <span>📥 XML 输入</span>
                </div>
                <div class="panel-actions">
                    <button class="btn" id="format-btn">
                        <span class="btn-icon">⚡</span> 格式化
                    </button>
                    <button class="btn" id="toggle-btn">
                        <span class="btn-icon">▼</span> 折叠
                    </button>
                </div>
            </div>
            <div class="panel-content">
                    <textarea id="xml-input" placeholder="在此输入 XML 内容...">&lt;?xml version="1.0" encoding="UTF-8"?>
&lt;catalog>
  &lt;book id="bk101">
    &lt;author>Gambardella, Matthew&lt;/author>
    &lt;title>XML Developer's Guide&lt;/title>
    &lt;genre>Computer&lt;/genre>
    &lt;price>44.95&lt;/price>
    &lt;publish_date>2000-10-01&lt;/publish_date>
    &lt;description>An in-depth look at creating applications with XML.&lt;/description>
    &lt;reviews>
      &lt;review date="2023-05-15" rating="5">Excellent resource for XML developers!&lt;/review>
      &lt;review date="2023-06-20" rating="4">Very comprehensive, but some sections could use more examples.&lt;/review>
    &lt;/reviews>
  &lt;/book>
  &lt;book id="bk102">
    &lt;author>Ralls, Kim&lt;/author>
    &lt;title>Midnight Rain&lt;/title>
    &lt;genre>Fantasy&lt;/genre>
    &lt;price>5.95&lt;/price>
    &lt;publish_date>2000-12-16&lt;/publish_date>
    &lt;description>A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world.&lt;/description>
    &lt;reviews>
      &lt;review date="2023-04-10" rating="5">Fantastic storytelling and world-building.&lt;/review>
    &lt;/reviews>
  &lt;/book>
&lt;/catalog></textarea>
            </div>
        </div>

        <div class="panel">
            <div class="panel-header">
                <div class="panel-title">
                    <span>📋 格式化结果</span>
                </div>
                <div class="panel-actions">
                    <button class="btn" id="copy-btn">
                        <span class="btn-icon">⎘</span> 复制结果
                    </button>
                </div>
            </div>
            <div class="panel-content">
                <div id="formatted-output">格式化结果将显示在这里...</div>
            </div>
        </div>
    </div>

    <div class="features">
        <div class="feature-card">
            <h3><span class="feature-icon">↕️</span> 可折叠输入区域</h3>
            <p>点击"折叠"按钮可以收起XML输入区域，让格式化结果占据更多空间，提升阅读体验。</p>
        </div>
        <div class="feature-card">
            <h3><span class="feature-icon">📱</span> 响应式设计</h3>
            <p>页面自动适应各种屏幕尺寸，在手机、平板和桌面设备上都能完美展示。</p>
        </div>
        <div class="feature-card">
            <h3><span class="feature-icon">🔤</span> 智能文本换行</h3>
            <p>自动处理长文本内容，确保所有XML值都能正确换行，避免水平滚动。</p>
        </div>
    </div>

    <footer>
        <p>© 2023 XML格式化工具 | 基于HTML5/CSS3/JavaScript</p>
    </footer>
</div>

<script>
    document.addEventListener('DOMContentLoaded', function() {
        const xmlInput = document.getElementById('xml-input');
        const formattedOutput = document.getElementById('formatted-output');
        const formatBtn = document.getElementById('format-btn');
        const toggleBtn = document.getElementById('toggle-btn');
        const copyBtn = document.getElementById('copy-btn');
        const inputPanel = document.getElementById('input-panel');

        // 初始格式化
        formatXML();

        // 格式化按钮事件
        formatBtn.addEventListener('click', formatXML);

        // 折叠/展开按钮事件
        toggleBtn.addEventListener('click', function() {
            inputPanel.classList.toggle('collapsed');
            toggleBtn.querySelector('.btn-icon').textContent =
                inputPanel.classList.contains('collapsed') ? '►' : '▼';
            toggleBtn.querySelector('span:last-child').textContent =
                inputPanel.classList.contains('collapsed') ? '展开' : '折叠';
        });

        // 复制按钮事件
        copyBtn.addEventListener('click', function() {
            const text = formattedOutput.textContent;
            navigator.clipboard.writeText(text).then(() => {
                const originalText = copyBtn.innerHTML;
                copyBtn.innerHTML = '<span class="btn-icon">✓</span> 已复制';
                setTimeout(() => {
                    copyBtn.innerHTML = originalText;
                }, 2000);
            });
        });

        // XML格式化函数
        function formatXML() {
            const xmlString = xmlInput.value.trim();

            if (!xmlString) {
                formattedOutput.innerHTML = '<div class="error">请输入XML内容</div>';
                return;
            }

            try {
                // 使用DOMParser解析XML
                const parser = new DOMParser();
                const xmlDoc = parser.parseFromString(xmlString, 'application/xml');

                // 检查解析错误
                const parserError = xmlDoc.getElementsByTagName('parsererror');
                if (parserError.length > 0) {
                    formattedOutput.innerHTML = `<div class="error">${parserError[0].innerHTML}</div>`;
                    return;
                }

                // 递归格式化XML节点
                formattedOutput.innerHTML = formatNode(xmlDoc.documentElement, 0);
            } catch (error) {
                formattedOutput.innerHTML = `<div class="error">${error.message}</div>`;
            }
        }

        // 递归格式化节点
        function formatNode(node, indentLevel) {
            const indent = '  '.repeat(indentLevel);
            let output = '';

            // 处理元素节点
            if (node.nodeType === Node.ELEMENT_NODE) {
                const tagName = node.tagName;
                const children = node.childNodes;
                const hasElements = hasElementChildren(node);

                // 开始标签
                output += `${indent}<span class="xml-tag">&lt;${tagName}</span>`;

                // 属性
                if (node.hasAttributes()) {
                    const attrs = node.attributes;
                    for (let i = 0; i < attrs.length; i++) {
                        const attr = attrs[i];
                        output += ` <span class="xml-attr-name">${attr.name}</span>=<span class="xml-attr-value">"${attr.value}"</span>`;
                    }
                }

                // 处理子节点
                if (children.length === 0) {
                    // 没有子节点 - 自闭合
                    output += `<span class="xml-tag">/></span>\n`;
                } else if (!hasElements && children.length === 1 && children[0].nodeType === Node.TEXT_NODE) {
                    // 只有文本内容 - 单行显示
                    const text = children[0].textContent.trim();
                    output += `<span class="xml-tag">></span><span class="xml-text">${escapeHtml(text)}</span><span class="xml-tag">&lt;/${tagName}></span>\n`;
                } else {
                    // 有子元素 - 多行显示
                    output += `<span class="xml-tag">></span>\n`;

                    for (let i = 0; i < children.length; i++) {
                        const child = children[i];

                        // 忽略空白文本节点
                        if (child.nodeType === Node.TEXT_NODE && child.textContent.trim() === '') {
                            continue;
                        }

                        output += formatNode(child, indentLevel + 1);
                    }

                    output += `${indent}<span class="xml-tag">&lt;/${tagName}></span>\n`;
                }
            }
            // 处理文本节点
            else if (node.nodeType === Node.TEXT_NODE) {
                const text = node.textContent.trim();
                if (text) {
                    output += `${indent}<span class="xml-text">${escapeHtml(text)}</span>\n`;
                }
            }
            // 处理注释节点
            else if (node.nodeType === Node.COMMENT_NODE) {
                output += `${indent}<span class="xml-comment">&lt;!--${node.textContent}--></span>\n`;
            }

            return output;
        }

        // 检查节点是否有元素子节点
        function hasElementChildren(node) {
            const children = node.childNodes;
            for (let i = 0; i < children.length; i++) {
                if (children[i].nodeType === Node.ELEMENT_NODE) {
                    return true;
                }
            }
            return false;
        }

        // HTML转义函数
        function escapeHtml(text) {
            return text
                .replace(/&/g, '&amp;')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;')
                .replace(/"/g, '&quot;')
                .replace(/'/g, '&#039;');
        }
    });
</script>
</body>
</html>
